package com.letoken.platform.admin.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.letoken.platform.admin.service.AdminUserService;
import com.letoken.platform.admin.service.CaptchaService;
import com.letoken.platform.admin.util.BracePlaceholder;
import com.letoken.platform.pub.constant.AdminUserInfoConstant;
import com.letoken.platform.pub.enums.ApiResponseEnum;
import com.letoken.platform.pub.enums.LetokenApiResponseEnum;
import com.letoken.platform.pub.exception.ApiServiceException;
import com.letoken.platform.pub.req.PageData;
import com.letoken.platform.pub.token.TokenUtil;
import com.letoken.platform.pub.util.AESUtil;
import com.letoken.platform.pub.util.BeanUtil;
import com.letoken.platform.pub.util.MD5Util;
import com.letoken.platform.repo.admin.dao.AdminRoleMapper;
import com.letoken.platform.repo.admin.dao.AdminRoleMenuMapper;
import com.letoken.platform.repo.admin.dao.AdminUserMapper;
import com.letoken.platform.repo.admin.dao.AdminUserRoleMapper;
import com.letoken.platform.repo.admin.po.AdminRole;
import com.letoken.platform.repo.admin.po.AdminUser;
import com.letoken.platform.repo.admin.po.AdminUserRole;
import com.letoken.platform.repo.admin.redis.AdminUserInfoRedis;
import com.letoken.platform.repo.admin.req.user.AddAdminAccountReq;
import com.letoken.platform.repo.admin.req.user.AdminUserReq;
import com.letoken.platform.repo.admin.req.user.QueryAdminAccountReq;
import com.letoken.platform.repo.admin.req.user.UpdateAdminAccountReq;
import com.letoken.platform.repo.admin.res.system.QueryAdminMenuTreeRes;
import com.letoken.platform.repo.admin.res.user.*;
import com.letoken.platform.repo.admin.util.MenuTreeUtil;
import com.letoken.platform.repo.app.redis.AdminLoginBlackListRedis;
import com.letoken.platform.repo.app.redis.AdminLoginLimitRedis;
import com.letoken.platform.repo.app.redis.AdminLoginWhiteListRedis;
import com.letoken.platform.repo.utlis.PageDataUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;

/**
 * @author letokenlabs
 * @date 2022/11/23$
 */
@Log4j2
@Service
public class AdminUserServiceImpl implements AdminUserService {


    @Autowired
    private AdminUserMapper adminUserMapper;

    @Autowired
    private AdminUserRoleMapper adminUserRoleMapper;

    @Autowired
    private AdminRoleMapper adminRoleMapper;

    @Autowired
    private AdminRoleMenuMapper adminRoleMenuMapper;


    @Autowired
    private RedisTemplate redisTemplate;

    String CAPTCHA_KEY = "captcha:{uuid}";


    /**
     * @param req
     * @return
     */
    @Override
    public AdminUserRes login(AdminUserReq req, String ip) {
        String username = req.getUsername();
        String password = req.getPassword().trim();
        String uuid = req.getUuid();
        String code = req.getCode();

        // 判断用户是否在黑名单
        if (AdminLoginBlackListRedis.me().hasKey(username)){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_033);
        }
        // 账号是否在白名单
        if (!AdminLoginWhiteListRedis.me().hasKey(username)){
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_033);
        }

        String key = BracePlaceholder.resolve(CAPTCHA_KEY, uuid);

        String text = (String) redisTemplate.opsForValue().get(key);

        if (ObjectUtils.isEmpty(text)){

            throw new ApiServiceException(ApiResponseEnum.ERROR_009);
        }
        if (!code.equalsIgnoreCase(text)) {

            throw new ApiServiceException(ApiResponseEnum.ERROR_010);
        }

        QueryWrapper<AdminUser> query = new QueryWrapper<>();
        query.eq("username", username);
        AdminUser adminUser = adminUserMapper.selectOne(query);

        if (adminUser == null) {
            log.error("userLogin failed, user is existence! userId is null");
            AdminLoginLimitRedis.me().incrementCounter("数据库未存在的账户（即黑客可能攻击的次数）");
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_033);
        }
        log.info("密码:"+adminUser.getPassword()+"     盐:"+ adminUser.getSalt()+"      明文密码:"+ password+"    加密密码和盐:"+MD5Util.MD5(password.trim() + adminUser.getSalt()));
        if (!adminUser.getPassword().equals(MD5Util.MD5(password.trim() + adminUser.getSalt()))) {
            AdminLoginLimitRedis.me().incrementCounter(username);
            log.error("adminLogin error. checkPassword fail. username={}, password={}", req.getUsername(), req.getPassword());
            Integer count = AdminLoginLimitRedis.me().getCount(username);
            // 密码错误5次进黑名单
            if (count >= 5) {
                AdminLoginBlackListRedis.me().setAdminUserName(username);
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_033);
            }
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_004);
        }
        // 账号是否被锁定
        if (adminUser.getLocked().equals(AdminUserInfoConstant.ADMIN_USER_LOGIN_LOCKED_ON)) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_009);
        }



        //判断token是否存在缓存中，若存在，则踢出
        String queryToken = adminUser.getToken();
        if (StringUtils.isNotBlank(queryToken)) {
            AdminUserInfoRedis.me().delete(queryToken);
        }

        //生成token，组装用户信息
        String token = TokenUtil.generateToken();

        //更加登录信息查询菜单页面按钮
        AdminUserInfoRes adminUserInfoRes = this.getAdminUserInfo(adminUser);

        //token作为key，AdminUserInfo作为value，缓存用户信息。
        AdminUserInfoRedis.me().setObject(token, adminUserInfoRes);

        //更新token到数据库
        AdminUser updateAdminUser = adminUser;
        updateAdminUser.setToken(token);
        updateAdminUser.setUpdateTime(LocalDateTime.now());
        updateAdminUser.setLoginTime(LocalDateTime.now());
        adminUserMapper.updateById(updateAdminUser);

        //返回token
        AdminUserRes res = new AdminUserRes();
        res.setToken(token);
        res.setName(adminUser.getName());
        res.setUsername(adminUser.getUsername());
        res.setUserId(adminUser.getAdminUserId());
        res.setAccess(adminUserInfoRes.getRoleInfo().getName());
        res.setTree(adminUserInfoRes.getTree());
        AdminLoginWhiteListRedis.me().addAdminUserName(username);
        return res;
    }


    /**
     * 新增管理员账号
     *
     * @param req)
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void addAccountManagement(AddAdminAccountReq req) {
        if (req.getPassword().length() < AdminUserInfoConstant.ADMIN_USER_PASSWORD_LENGTH_MIN || req.getPassword().length() > AdminUserInfoConstant.ADMIN_USER_PASSWORD_LENGTH_MAX) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_007);
        }
        // 判断角色是否存在
        LambdaQueryWrapper<AdminRole> roleQueryWrapper = Wrappers.lambdaQuery();
        roleQueryWrapper.eq(AdminRole::getAdminRoleId, req.getAdminRoleId());
        Integer flag = adminRoleMapper.selectCount(roleQueryWrapper);
        if (flag <= 0) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_031);
        }

        LambdaQueryWrapper<AdminUser> userQueryWrapper = Wrappers.lambdaQuery();
        userQueryWrapper.eq(AdminUser::getUsername, req.getUsername());
        AdminUser adminUser = adminUserMapper.selectOne(userQueryWrapper);
        if (Objects.nonNull(adminUser)) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_006);
        }

        adminUser = new AdminUser();
        String passwordSalt = AESUtil.salt();
        String password = MD5Util.MD5(req.getPassword().trim() + passwordSalt);

        adminUser.setUsername(req.getUsername());
        adminUser.setName(req.getRealName());
        adminUser.setPassword(password);
        adminUser.setSalt(passwordSalt);
        adminUser.setLocked(AdminUserInfoConstant.ADMIN_USER_LOGIN_LOCKED_OFF);
        adminUser.setCreateTime(LocalDateTime.now());
        adminUser.setIsDelete(AdminUserInfoConstant.NOT_DELETE_STATUS);
        adminUserMapper.insert(adminUser);

        AdminUserRole adminUserRole = new AdminUserRole();
        adminUserRole.setAdminUserId(adminUser.getAdminUserId());
        adminUserRole.setAdminRoleId(req.getAdminRoleId());
        adminUserRole.setCreateTime(LocalDateTime.now());
        adminUserRole.setUpdateTime(LocalDateTime.now());
        adminUserRole.setIsDelete(AdminUserInfoConstant.NOT_DELETE_STATUS);
        adminUserRoleMapper.insert(adminUserRole);
        AdminLoginWhiteListRedis.me().addAdminUserName(adminUser.getUsername());
    }

    /**
     * 查询管理账号列表
     *
     * @param req
     * @return
     */
    @Override
    public PageData<QueryAdminAccountRes> queryAdminAccount(QueryAdminAccountReq req) {
        Page<QueryAdminAccountRes> page = new Page<>(req.getPageReq().getPage(), req.getPageReq().getSize());
        Page<QueryAdminAccountRes> res = adminUserMapper.queryAdminAccount(page, req);
        return PageDataUtil.getPageData(res);
    }

    /**
     * 修改管理账号
     *
     * @param req
     */
    @Override
    public void UpdateAccountManagement(UpdateAdminAccountReq req) {
        if (req.getPassword().length() < AdminUserInfoConstant.ADMIN_USER_PASSWORD_LENGTH_MIN || req.getPassword().length() > AdminUserInfoConstant.ADMIN_USER_PASSWORD_LENGTH_MAX) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_007);
        }

        // 判断角色是否存在
        LambdaQueryWrapper<AdminRole> roleQueryWrapper = Wrappers.lambdaQuery();
        roleQueryWrapper.eq(AdminRole::getAdminRoleId, req.getAdminRoleId());
        Integer flag = adminRoleMapper.selectCount(roleQueryWrapper);
        if (flag <= 0) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_031);
        }

        LambdaQueryWrapper<AdminUser> userQueryWrapper = Wrappers.lambdaQuery();
        userQueryWrapper.eq(AdminUser::getUsername, req.getUsername());
        AdminUser adminUser = adminUserMapper.selectOne(userQueryWrapper);
        if (Objects.isNull(adminUser)) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_ADMIN_USER_030);
        }

        adminUser = new AdminUser();
        String passwordSalt = AESUtil.salt();
        String password = MD5Util.MD5(req.getPassword().trim() + passwordSalt);

        adminUser.setAdminUserId(req.getAdminUserId());
        adminUser.setUsername(req.getUsername());
        adminUser.setName(req.getRealName());
        adminUser.setPassword(password);
        adminUser.setSalt(passwordSalt);
        adminUser.setLocked(AdminUserInfoConstant.ADMIN_USER_LOGIN_LOCKED_OFF);
        adminUser.setCreateTime(LocalDateTime.now());
        adminUser.setIsDelete(AdminUserInfoConstant.NOT_DELETE_STATUS);
        adminUserMapper.updateById(adminUser);

        // 根据管理用户id修改他的角色id
        AdminUserRole adminUserRole = new AdminUserRole();
        adminUserRole.setAdminRoleId(req.getAdminRoleId());
        adminUserRole.setIsDelete(AdminUserInfoConstant.NOT_DELETE_STATUS);
        LambdaUpdateWrapper<AdminUserRole> userRoleWrapper = Wrappers.lambdaUpdate();
        userRoleWrapper.eq(AdminUserRole::getAdminUserId, req.getAdminUserId());
        adminUserRoleMapper.update(adminUserRole, userRoleWrapper);

    }

    /**
     * 删除管理账号
     *
     * @param adminUserId
     */
    @Override
    public void deleteAccountManagement(Integer adminUserId) {
        AdminUser adminUser = adminUserMapper.selectById(adminUserId);
        if (adminUser != null){
            AdminUserInfoRedis.me().delete(adminUser.getToken());
            AdminLoginWhiteListRedis.me().removeAdminUserName(adminUser.getUsername());
            adminUserMapper.deleteById(adminUserId);
        }
    }

    /**
     * 启用管理账号
     *
     * @param adminUserId
     */
    @Override
    public void EnableAccountManagement(Integer adminUserId) {
        AdminUser adminUser = new AdminUser();
        adminUser.setAdminUserId(adminUserId);
        adminUser.setUpdateTime(LocalDateTime.now());
        adminUser.setLocked(AdminUserInfoConstant.LOCKED_ENABLE);
        adminUserMapper.updateById(adminUser);
    }

    /**
     * 禁用管理账号
     *
     * @param adminUserId
     */
    @Override
    public void disableAccountManagement(Integer adminUserId) {
        AdminUser adminUser = adminUserMapper.selectById(adminUserId);
        AdminUserInfoRedis.me().delete(adminUser.getToken());
        adminUser.setUpdateTime(LocalDateTime.now());
        adminUser.setLocked(AdminUserInfoConstant.LOCKED_DISABLE);
        String token = TokenUtil.generateToken();
        adminUser.setToken(token);
        adminUserMapper.updateById(adminUser);

    }

    /**
     * 根据id查询管理账号
     *
     * @param adminUserId
     * @return
     */
    @Override
    public QueryAdminAccountByIdRes queryAdminAccountById(Integer adminUserId) {
        QueryAdminAccountByIdRes res;
        res = adminUserMapper.queryAdminAccountById(adminUserId);
        if (Objects.nonNull(res)) {
            res.setAdminUserId(adminUserId);
        }
        return res;
    }


    public AdminUserInfoRes getAdminUserInfo(AdminUser adminUser) {
        AdminUserInfoRes res = new AdminUserInfoRes();
        BeanUtil.quickCopy(adminUser, res);

        //查询用户的角色列表
        AdminUserRoleInfoRes roleInfoRes = adminUserRoleMapper.selectRoleListByAdminUserId(res.getAdminUserId());
        res.setRoleInfo(roleInfoRes);

        //查询用户的菜单列表
        if (ObjectUtil.isNotNull(roleInfoRes)) {
            List<String> menuUriList = adminRoleMenuMapper.getMenuListByAdminUserRoleId(roleInfoRes.getAdminRoleId());
            res.setMenuUriList(menuUriList);
            List<QueryAdminMenuTreeRes> userMenuList = adminRoleMenuMapper.getMenuListByRole(roleInfoRes.getAdminRoleId());
            List<QueryAdminMenuTreeRes> treeList = MenuTreeUtil.buildMenuTree(userMenuList);
            res.setTree(treeList);
//            List<String> authFlagList = new ArrayList<>(userMenuList).stream().map(QueryAdminMenuTreeRes::getAuthFlag).filter(StringUtils::isNotBlank).collect(Collectors.toList());
//            res.setAuthFlagList(authFlagList);
        }
        return res;
    }
}
